package org.os.framework.context.config;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.os.framework.context.model.HttpException;
import org.os.framework.context.model.ResponseBody;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

/**
 * 全局响应处理
 * @author
 * 
 */
@Slf4j
@RestControllerAdvice
public class HttpResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Resource
    private SecurityConfig security;

    @SuppressWarnings("null")
    @Override
    public boolean supports(MethodParameter returnType,
            Class<? extends HttpMessageConverter<?>> converterType) {
        // 对正常响应结果进行封装，异常全部使用ResponseEntity封装
        return ResponseEntity.class != returnType.getParameterType();
    }

    @SuppressWarnings("null")
    @Override
    @Nullable
    public Object beforeBodyWrite(@Nullable Object body,
            MethodParameter returnType,
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request,
            ServerHttpResponse response) {
        if (security.getEncrypt()) {
            body = security.encrypt(body instanceof String ? body.toString() : JSON.toJSONString(body));
        }
        ResponseBody data = ResponseBody.builder()
                .success(true)
                .status(HttpStatus.OK.name())
                .data(body)
                .build();
        // 如果类型不是string直接返回
        if (String.class != returnType.getParameterType()) {
            return data;
        }
        // 如果类型是string转成json
        HttpHeaders headers = response.getHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        try {
            return new ObjectMapper().writeValueAsString(data);
        } catch (Exception e) {
            log.error("ObjectMapper().writeValueAsString error =>\n", e);
            return "";
        }
    }

    /**
     * 自定义异常
     * 
     * @param exception
     * @return
     */
    @ExceptionHandler(HttpException.class)
    public ResponseEntity<ResponseBody> httpException(HttpException exception) {
        log.error("自定义异常 =>\n", exception);
        return ResponseEntity.status(HttpStatus.OK.value()).body(
                ResponseBody.builder()
                        .success(false)
                        .status(exception.getStatus())
                        .message(exception.getMessage())
                        .build());
    }

    /**
     * 400
     * 
     * @param exception
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<ResponseBody> missingServletRequestParameterException(
            MissingServletRequestParameterException exception) {
        log.error("Error 400 (缺少required参数) =>\n", exception);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
                ResponseBody.builder()
                        .success(false)
                        .status(HttpStatus.BAD_REQUEST.name())
                        .message(exception.getMessage())
                        .build());
    }

    /**
     * 400
     * 
     * @param exception
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<ResponseBody> notReadableException(HttpMessageNotReadableException exception) {
        log.error("Error 400 (参数类型错误) =>\n", exception);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
                ResponseBody.builder()
                        .success(false)
                        .status(HttpStatus.BAD_REQUEST.name())
                        .message(exception.getMessage())
                        .build());
    }

    /**
     * 400
     * 
     * @param exception
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ResponseBody> methodArgumentNotValidException(
            MethodArgumentNotValidException exception) {
        log.error("Error 400 (参数校验失败) =>\n", exception);
        List<FieldError> errors = exception.getBindingResult().getFieldErrors();
        Map<String, String> map = errors.stream().collect(Collectors.toMap(FieldError::getField,
                FieldError::getDefaultMessage, (o, n) -> n));
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
                ResponseBody.builder()
                        .success(false)
                        .status(HttpStatus.BAD_REQUEST.name())
                        .message(errors.isEmpty() ? "请求参数不符合输入要求"
                                : errors.get(0).getDefaultMessage())
                        .data(map)
                        .build());
    }

    /**
     * 404
     * 需配置
     * spring.mvc.throw-exception-if-no-handler-found = true
     * spring.resources.add-mappings = false
     * 且导致swagger无效，需自定义实现WebMvcConfigurer
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<ResponseBody> noHandlerFound(NoHandlerFoundException exception) {
        log.error("Error 404 =>\n", exception);
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
                ResponseBody.builder()
                        .success(false)
                        .status(HttpStatus.NOT_FOUND.name())
                        .message(exception.getMessage())
                        .build());
    }

    /**
     * 405
     * 
     * @param exception
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<ResponseBody> methodNotSupport(HttpRequestMethodNotSupportedException exception) {
        log.error("Error 405 =>\n", exception);
        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(
                ResponseBody.builder()
                        .success(false)
                        .status(HttpStatus.METHOD_NOT_ALLOWED.name())
                        .message(exception.getMessage())
                        .build());
    }

    /**
     * 500
     * 
     * @param exception
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ResponseBody> httpError(Exception exception) {
        log.error("Error 500 =>\n", exception);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
                ResponseBody.builder()
                        .success(false)
                        .status(HttpStatus.INTERNAL_SERVER_ERROR.name())
                        .message("系统异常，请稍后再试...")
                        .build());
    }

}
